﻿using ICSharpCode.SharpZipLib.Zip;
using Microsoft.Extensions.Caching.Memory;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace Bot.Domain
{
    public class Utils
    {
        public static string GenerateDialogflowKey(int siteId, Guid botId)
        {
            return string.Format("GoogleCredJson_{0}_{1}", siteId, botId);
        }
        public static void UnZip(Stream sm, string zipedFolder)
        {
            using (ZipInputStream s = new ZipInputStream(sm))
            {
                ZipEntry theEntry;
                while ((theEntry = s.GetNextEntry()) != null)
                {
                    string directoryName = Path.Combine(zipedFolder, Path.GetDirectoryName(theEntry.Name));
                    string fileName = Path.GetFileName(theEntry.Name);

                    // create directory
                    if (directoryName.Length > 0 && !Directory.Exists(directoryName))
                    {
                        Directory.CreateDirectory(directoryName);
                    }

                    if (fileName != String.Empty)
                    {
                        using (FileStream streamWriter = File.Create(Path.Combine(directoryName, fileName)))
                        {

                            int size = 2048;
                            byte[] data = new byte[2048];
                            while (true)
                            {
                                size = s.Read(data, 0, data.Length);
                                if (size > 0)
                                {
                                    streamWriter.Write(data, 0, size);
                                }
                                else
                                {
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }
        public static string Zip(string sourceDirectory)
        {
            string zipPath = string.Format("{0}.zip", sourceDirectory);
            FastZip fastZip = new FastZip();
            fastZip.CreateZip(zipPath, sourceDirectory, true, "");
            return zipPath;
        }
        public static string WriteNewFile(string path, string context)
        {
            using(FileStream fs = File.Open(path, FileMode.OpenOrCreate))
            {
                byte[] buff = Encoding.UTF8.GetBytes(context);
                fs.Write(buff, 0, buff.Length);
                return path;
            }
        }
        public static string GetFileContent(string path, string encoding="UTF-8")
        {
            if (File.Exists(path))
            {
                using(StreamReader sr = new StreamReader(path, Encoding.GetEncoding(encoding)))
                {
                    return sr.ReadToEnd();
                }
            }
            else
            {
                throw new Exception("file not exists.");
            }
        }

        /// <summary>
        /// 字符串间隔转小写，去掉空格
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string[] TextSplitTrim(string text, string separator, bool toLower=false)
        {
            string[] arry = text.Split(new string[] { separator }, StringSplitOptions.RemoveEmptyEntries);
            if (arry != null && arry.Length > 0)
            {
                for (int i = 0; i < arry.Length; i++)
                {
                    arry[i] = TextTrim(arry[i],toLower);
                }
            }
            //text = string.Join(separator, arry);
            return arry;
        }
        /// <summary>
        /// 字符串转小写,去掉多余的空格
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string TextTrim(string text, bool toLower = false)
        {
            if (string.IsNullOrWhiteSpace(text))
            {
                return "";
            }
            else
            {
                string[] arry = text.Trim().Replace("\n", "").Replace("\r", "").Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                if (toLower)
                {
                    arry = arry.Select(a => a.ToLower()).ToArray();
                }
                text = string.Join(" ", arry);
                return text;
            }
        }
        /// <summary>
        /// 去掉字符串空格 含 Ascii 160 空格
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string TrimSpace(string text)
        {
            byte[] utf8Space = new byte[] { 0xc2, 0xa0 };
            string tempSpace = System.Text.Encoding.GetEncoding("UTF-8").GetString(utf8Space);
            return text.Replace(" ", "").Replace(tempSpace, "");
        }

        /// <summary>
        /// 通过xsd验证xml格式是否正确，正确返回空字符串，错误返回提示  
        /// </summary>
        /// <param name="xml">xml</param>
        /// <param name="xsd">xsd</param>
        /// <param name="nameSpaceUrl">命名空间，无则默认为null</param>
        /// <returns></returns>
        public static bool XmlValidationByXsd(string xml, string xsd,
            Dictionary<string, int> filedLenthDic, out IList<string> errorMessages, string nameSpaceUrl = null)
        {
            errorMessages = new List<string>();
            IList<string> messages = new List<string>();
            
            using(XmlReader schemaDocument = XmlReader.Create(new StringReader(xsd)))
            {
                XmlReaderSettings settings = new XmlReaderSettings();
                settings.ValidationType = ValidationType.Schema;
                settings.Schemas.Add(nameSpaceUrl, schemaDocument);
                settings.ValidationEventHandler += (x, y) =>
                {
                    string error = y.Message;
                    if (error.Contains("MaxLength"))
                    {
                        int firstIndex = error.IndexOf("'")+1;
                        int length = error.Substring(firstIndex).IndexOf("'");
                        string field = error.Substring(firstIndex, length);

                        if (filedLenthDic.ContainsKey(field))
                        {
                            int maxLength = filedLenthDic[field];
                            error = error.Replace("MaxLength", maxLength.ToString());
                        }
                    }
                    messages.Add(string.Format("{0},LineRow:{1}", error, y.Exception.LineNumber));
                };
                using (XmlReader xmlReader = XmlReader.Create(new StringReader(xml), settings))
                {
                    try
                    {
                        while (xmlReader.Read()) ;
                    }
                    catch (XmlException ex)
                    {
                        messages.Add(string.Format("{0}", ex.Message));
                    }   
                }
            }

            errorMessages = messages;
            return !(messages.Count > 0);
        }
        public static string XmlSerialize<T>(T obj)
        {
            using (StringWriter sw = new StringWriter())
            {
                Type t = obj.GetType();
                XmlSerializer serializer = new XmlSerializer(obj.GetType());
                serializer.Serialize(sw, obj);
                return sw.ToString();
            }
        }
        public static T XmlDeserialize<T>(string xml) where T : class
        {
            try
            {
                using (StringReader sr = new StringReader(xml))
                {
                    XmlSerializer serializer = new XmlSerializer(typeof(T));
                    return serializer.Deserialize(sr) as T;
                }
            }
            catch (Exception ex)
            {
                return null;
            }
        }
        public static bool JsonValidationBySchema(string json, string schema, out IList<string> errorMessages)
        {
            var jSchema = JsonSchema.Parse(schema);
            var jObject = JObject.Parse(json);

            return jObject.IsValid(jSchema, out errorMessages);
        }

        /// <summary>
        /// 获取嵌入资源
        /// </summary>
        /// <param name="type"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public static string GetResourceContent(Type type, string name)
        {
            Assembly assembly = Assembly.GetAssembly(type);
            string projectName = assembly.GetName().Name;
            string resourceName = string.Format("{0}.{1}", projectName, name);

            using (Stream sm = assembly.GetManifestResourceStream(resourceName))
            {
                int numBytesToRead = (int)sm.Length;
                byte[] buffer = new byte[numBytesToRead];
                sm.Read(buffer, 0, buffer.Length);
                return Encoding.UTF8.GetString(buffer);
            }
        }
        /// <summary>
        /// 通用分隔符
        /// </summary>
        public static readonly string GeneralSeparator = "⊙";
        private static readonly MemoryCache cache = new MemoryCache(new MemoryCacheOptions());
        /// <summary>
        /// 获取MemoryCache的值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static T GetMemoryCache<T>(string key)
        {
            T t = (T)cache.Get(key);
            return t;
        }
        public static T GetMemoryCache<T>(string key, Func<T> func, int timeout = 30)
        {
            T t = GetMemoryCache<T>(key);
            if(t == null)
            {
                t = func();
                SetMemoryCache(key, t, timeout);
            }
            return t;
        }
        /// <summary>
        /// 设置MemoryCache
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static void SetMemoryCache<T>(string key, T value)
        {
            cache.Set<T>(key, value, new DateTimeOffset(DateTime.UtcNow.AddDays(1)));
        }
        /// <summary>
        /// 设置MemoryCache
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static void SetMemoryCache<T>(string key, T value, int timeout)
        {
            cache.Set<T>(key, value, new DateTimeOffset(DateTime.UtcNow.AddMinutes(timeout)));
        }
        public static void RemoveMemoryCache(string key)
        {
            cache.Remove(key);
        }
        public static T GetRandomItemFromList<T>(ICollection<T> list)
        {
            T t = default(T);
            if (list != null)
            {
                int maxValue = list.Count;
                Random random = new Random(Guid.NewGuid().GetHashCode());
                int index = random.Next(maxValue);
                t = list.ToList()[index];
            }
            return t;
        }
        /// <summary>
        /// 获取字符串中制定标签的值
        /// </summary>
        /// <param name="str">字符串</param>
        /// <param name="title">标签</param>
        /// <param name="attrib">属性</param>
        /// <returns></returns>
        public static string GetTitleContent(string str, string title, string attrib)
        {
            string pattern = string.Format("<{0}[^>]*?{1}=(['\"\"]?)(?<url>[^'\"\">]+)\\1[^>]*>", title, attrib);
            System.Text.RegularExpressions.Match match = System.Text.RegularExpressions.Regex.Match(str, pattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            string result = match.Groups["url"].Value;
            if (string.IsNullOrEmpty(result))
            {
                result = "";
            }
            return result;
        }
        public static Guid? ConvertGuid(string str)
        {
            if (string.IsNullOrWhiteSpace(str)) return null;
            Guid temp;
            if (Guid.TryParse(str, out temp)) return temp;
            return null;
        }
        public static Guid? ConvertGuid(Guid guid)
        {
            if (guid == Guid.Empty) return null;
            return guid;
        }
        public static string CreateMD5Hash(string input)
        {
            MD5 md5 = MD5.Create();
            byte[] inputBytes = Encoding.ASCII.GetBytes(input);
            byte[] hashBytes = md5.ComputeHash(inputBytes);
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < hashBytes.Length; i++)
            {
                sb.Append(hashBytes[i].ToString("X2"));
            }
            return sb.ToString();
        }
    }
}
